#include "precompile.h"

#include <vsys>

#include "SectionFileConfig.h"

#include "SectionFileStream.h"

#ifdef _DEBUG
#define new DEBUG_NEW
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;
#endif

SectionFileStream::SectionFileStream() :
	m_iStartSection(0),
	m_lCurrentPos(0),
	m_lEndPos(0),
	m_lSectionBytes(0),
	m_lMaxFileLength(0),
	m_bRecycled(0),
	m_sTempFileName(L""),
	m_lDataLenInTrsct(0)
{

}

SectionFileStream::~SectionFileStream()
{
	Close();
}

vresult SectionFileStream::OpenTempFile(const wchar_t* sPrefix,long lSectionBytes)
{
	m_lSectionBytes=lSectionBytes;
	m_lMaxFileLength=m_lSectionBytes*MAX_SECTION_NUM;
	wchar_t* sTempFileName=::_wtempnam(NULL,sPrefix);
	m_sTempFileName=sTempFileName;
	delete[] sTempFileName;sTempFileName=NULL;
	CHECK_RETURN(m_simpleFileStream.OpenFile(m_sTempFileName.c_str(),true,true));
	m_iStartSection=0;
	m_lCurrentPos=0;
	m_lEndPos=0;
//	m_simpleFileStream.SetStreamLength(0);
	m_bRecycled=0;
	return vok;
}

vresult SectionFileStream::Close()
{
	if(m_lSectionBytes>0)
	{
		CHECK_RETURN(m_simpleFileStream.Close());
		::DeleteFile(m_sTempFileName.c_str());
		m_sTempFileName.clear();
		m_lSectionBytes=0;
		m_iStartSection=0;
		m_lCurrentPos=0;
		m_lEndPos=0;
		m_lSectionBytes=0;
		m_bRecycled=0;
	}
	return vok;
}

int32 SectionFileStream::GetSectionCount()
{
	if(m_bRecycled)
		return MAX_SECTION_NUM;
	else
		return m_lEndPos/m_lSectionBytes+1;
}

vresult SectionFileStream::SaveByte(byte by)
{
	long lDataSave;
	return SaveData(&by,sizeof(byte),&lDataSave);
}

vresult SectionFileStream::LoadByte(byte* pByte)
{
	long lDataLoad;
	return LoadData(pByte,sizeof(byte),&lDataLoad);
}

vresult SectionFileStream::SaveData(const byte* pByte,long lDataLen,long* pDataSaved)
{
	bool bUpdateEndPos = (m_lCurrentPos == m_lEndPos);
	if (m_lCurrentPos + lDataLen > m_lMaxFileLength)
	{
		byte* pBytePart = (byte*) pByte;
		long lDataLenPart = m_lMaxFileLength - m_lCurrentPos;
		{
			CHECK_RETURN(m_simpleFileStream.SaveData(pBytePart, lDataLenPart, pDataSaved));
			ASSERT(*pDataSaved == lDataLenPart);

			pBytePart += lDataLenPart;
			m_lCurrentPos = 0;
			m_simpleFileStream.Seek(IFileStream::kBegin, 0);
			m_bRecycled ++;
		}

		CHECK_RETURN(m_simpleFileStream.SaveData(pBytePart, lDataLen - lDataLenPart, pDataSaved));
		ASSERT(*pDataSaved == lDataLen - lDataLenPart);
		m_lCurrentPos += *pDataSaved;
		m_lDataLenInTrsct+=lDataLen;
		if (bUpdateEndPos)
			m_lEndPos = m_lCurrentPos;

		*pDataSaved = lDataLen;
		return vok;
	}

	*pDataSaved=0;
	CHECK_RETURN(m_simpleFileStream.SaveData(pByte,lDataLen,pDataSaved));
	ASSERT(*pDataSaved==lDataLen);
	m_lCurrentPos+=*pDataSaved;
	m_lDataLenInTrsct+=lDataLen;
	if (bUpdateEndPos)
		m_lEndPos = m_lCurrentPos;
	
	return vok;
}

vresult SectionFileStream::LoadData(byte* pByte,long lDataLen,long* pDataLoaded)
{
	if (m_lCurrentPos + lDataLen > m_lMaxFileLength)
	{
		byte* pBytePart = pByte;
		long lDataLenPart = m_lMaxFileLength - m_lCurrentPos;
		{
			CHECK_RETURN(m_simpleFileStream.LoadData(pBytePart, lDataLenPart, pDataLoaded));
			ASSERT(*pDataLoaded == lDataLenPart);

			pBytePart += lDataLenPart;
			m_lCurrentPos = 0;
			m_simpleFileStream.Seek(IFileStream::kBegin, 0);
		}

		CHECK_RETURN(m_simpleFileStream.LoadData(pBytePart, lDataLen - lDataLenPart, pDataLoaded));
		ASSERT(*pDataLoaded == lDataLen - lDataLenPart);
		m_lCurrentPos += *pDataLoaded;

		*pDataLoaded = lDataLen;
		return vok;
	}
	
	CHECK_RETURN(m_simpleFileStream.LoadData(pByte,lDataLen,pDataLoaded));
	ASSERT(*pDataLoaded==lDataLen);
	m_lCurrentPos+=*pDataLoaded;

	return vok;
}

vresult SectionFileStream::Seek(kSeekMode mode,long nNewPos)
{
	switch (mode)
	{
	case IFileStream::kBegin:
		m_lCurrentPos=m_iStartSection*m_lSectionBytes+nNewPos;
		break;
	case IFileStream::kCurrent:
		m_lCurrentPos+=nNewPos;
		break;
	case IFileStream::kEnd:
		m_lCurrentPos=m_lEndPos+nNewPos;
		break;
	default:
		ASSERT(false);
	}

	if (m_lCurrentPos > m_lMaxFileLength)
		m_lCurrentPos %= m_lMaxFileLength;

	m_simpleFileStream.Seek(IFileStream::kBegin,m_lCurrentPos);
	return vok;
}

vresult SectionFileStream::SetStreamLength(long /*lNewLength*/)
{
	return vok;
}

bool SectionFileStream::IsDataLenInTrsctLongThanFileLen()const
{
	return (m_lDataLenInTrsct>=m_lMaxFileLength);
}

long SectionFileStream::RemoveHead(long lRemoveBytes)
{
	if (!m_bRecycled)
		return 0;

	int32 iRemoveSections = 0;
	int32 iCurrentSection = m_lCurrentPos / m_lSectionBytes;
	int32 lPreviousPos = (m_lCurrentPos - lRemoveBytes + m_lMaxFileLength) % m_lMaxFileLength;
	int32 lStartPos = m_iStartSection * m_lSectionBytes;
	if (m_lCurrentPos >= lStartPos)
	{
		if ((lPreviousPos < lStartPos)
			|| (lPreviousPos >= lStartPos && lPreviousPos > m_lCurrentPos))
		{
			iRemoveSections = iCurrentSection - m_iStartSection + 1;
		}
	}
	else if (lPreviousPos < lStartPos && lPreviousPos > m_lCurrentPos)
	{
		iRemoveSections = iCurrentSection + MAX_SECTION_NUM - m_iStartSection + 1;
	}

	ASSERT(iRemoveSections <= MAX_SECTION_NUM);
	if (iRemoveSections > 0)
	{
		m_iStartSection += iRemoveSections;
		m_iStartSection %= MAX_SECTION_NUM;
		return iRemoveSections * m_lSectionBytes;
	}

	return 0;
}
